home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / lang / hfiles01 / hfiles.c < prev    next >
Text File  |  1994-09-21  |  14KB  |  696 lines

  1. /*
  2.  *    hfiles.c
  3.  *
  4.  *    This program reads through source files, generating a list of
  5.  *    dependencies suitable for inclusion in a makefile.  It can handle
  6.  *    C-style include syntax (#include "file") or assembler-style
  7.  *    syntax (include file), and can be easily modified to support other
  8.  *    syntaxes.
  9.  *
  10.  *    This code is copyright (c) 1994, Kevin D. Hunter.  It is expressly
  11.  *    placed in the public domain for non-commercial use, without fee.
  12.  *    Commercial redistribution of this source code, or object code derived
  13.  *    from it is not permitted.
  14.  *
  15.  *    Please use this code in the spirit in which it is intended.  If you
  16.  *    have suggestions for improvements, I would love to hear them.
  17.  *    I can be reached as hunterkd@aol.com
  18.  *
  19.  *    Known deficiencies:
  20.  *      -    If you use a command file (@file), you must put only one option,
  21.  *        file or whatever on each line.  In addition, wild cards are not
  22.  *        expanded from within command file.
  23.  *
  24.  *      -    There is no support for include paths.  Right now, if an include
  25.  *        file is not in the current directory and does not have path info
  26.  *        in the include line, the program will not include it in the
  27.  *        dependency list, and will print a warning about not being able
  28.  *        to open the file.
  29.  */
  30.  
  31. #include <assert.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35.  
  36. #define FALSE    0
  37. #define TRUE    1
  38. #define LINE_BUFFER_SIZE    256
  39. #define DEFAULT_ALLOC        16
  40. #define MAX_FILE_LEN        12
  41.  
  42. typedef struct sExtension
  43. {
  44.     char *            srcExt;            /* Extension of the source file */
  45.     size_t            srcExtLen;        /* Length of the source file extension */
  46.     char *            objExt;            /* Extension of the corresponding object file */
  47.     size_t            objExtLen;        /* Length of the object file extension */
  48.     unsigned int    scanner;        /* Which scanner type to use */
  49. } Extension;
  50.  
  51. #define SCANNER_EOL        0
  52. #define SCANNER_CTYPE    1            /* "C" style */
  53. #define SCANNER_ATYPE    2            /* ASM style */
  54.  
  55. /*
  56.  *    This table has both upper and lower case versions of the extensions,
  57.  *    because the Microsoft "SETARGV.OBJ" (the command line wild-card expander)
  58.  *    has a nasty tendency to put file names into upper case.  If you use
  59.  *    this in another environment, you may need to fix this.
  60.  */
  61.  
  62. const Extension ExtList[] =
  63. {
  64.     {    ".c",    2, ".obj", 4,    SCANNER_CTYPE    },
  65.     {    ".cc",    3, ".obj", 4,    SCANNER_CTYPE    },
  66.     {    ".cxx",    4, ".obj", 4,    SCANNER_CTYPE    },
  67.     {    ".cpp",    4, ".obj", 4,    SCANNER_CTYPE    },
  68.  
  69.     {    ".C",    2, ".OBJ", 4,    SCANNER_CTYPE    },
  70.     {    ".CC",    3, ".OBJ", 4,    SCANNER_CTYPE    },
  71.     {    ".CXX",    4, ".OBJ", 4,    SCANNER_CTYPE    },
  72.     {    ".CPP",    4, ".OBJ", 4,    SCANNER_CTYPE    },
  73.  
  74.     {    ".asm",    4, ".obj", 4,    SCANNER_ATYPE    },
  75.     {    ".s",    2, ".obj", 4,    SCANNER_ATYPE    },
  76.  
  77.     {    ".ASM",    4, ".OBJ", 4,    SCANNER_ATYPE    },
  78.     {    ".S",    2, ".OBJ", 4,    SCANNER_ATYPE    },
  79.  
  80.     {    ".rc",    3, ".res", 4,    SCANNER_CTYPE    },
  81.     {    ".RC",    3, ".RES", 4,    SCANNER_CTYPE    },
  82.  
  83.     {    "",        0, "",       0,    SCANNER_EOL        }
  84. };
  85.  
  86. /*
  87.  *    This is where the default values for options are set.
  88.  */
  89.  
  90. unsigned int TabSize = 4;
  91. unsigned int LineWidth = 80;
  92. unsigned int FileSlotsAvail = 0;
  93. unsigned int FileSlotsUsed = 0;
  94. char **FileSlot = (char **)NULL;
  95.  
  96. /*
  97.  *    This routine strips the trailing \n (or \r\n) off a line.  If no
  98.  *    \n or \r\n is found, the input line must have been truncated by
  99.  *    fgets, so it returns FALSE to indicate that it wasn't a "whole"
  100.  *    line.
  101.  */
  102.  
  103. int
  104. WholeLine(char *p)
  105. {
  106.     while(*p != '\0')
  107.     {
  108.         if (*p == '\r' || *p == '\n')
  109.         {
  110.             *p = '\0';
  111.             return(TRUE);
  112.         }
  113.  
  114.         ++p;
  115.     }
  116.  
  117.     return(FALSE);
  118. }
  119.  
  120. /*
  121.  *    This routine initializes the dependencies storage stuff.  This is
  122.  *    a dynamically allocated and extended list of pointers, each of which
  123.  *    contains a dynamically allocated string (file name).
  124.  */
  125.  
  126. int
  127. InitDependencies(void)
  128. {
  129.     unsigned int i;
  130.  
  131.     FileSlot = malloc(DEFAULT_ALLOC * sizeof(char *));
  132.     if (FileSlot == (char **)NULL)
  133.     {
  134.         (void)fprintf(stderr, "Out of memory\n");
  135.         return(FALSE);
  136.     }
  137.  
  138.     for (i = 0; i < DEFAULT_ALLOC; i++)
  139.     {
  140.         FileSlot[i] = (char *)NULL;
  141.     }
  142.  
  143.     FileSlotsUsed = 0;
  144.     FileSlotsAvail = DEFAULT_ALLOC;
  145.  
  146.     return(TRUE);
  147. }
  148.  
  149. /*
  150.  *    This routine clears out accumulated data in the dependencies area.
  151.  *    It doesn't delete the list itself, only the attached strings.
  152.  */
  153.  
  154. void
  155. ClearDependencies(void)
  156. {
  157.     unsigned int i;
  158.  
  159.     if (FileSlot == (char **)NULL)
  160.     {
  161.         return;
  162.     }
  163.  
  164.     for (i = 0; i < FileSlotsUsed; i++)
  165.     {
  166.         if (FileSlot[i] != (char *)NULL)
  167.         {
  168.             free(FileSlot[i]);
  169.             FileSlot[i] = (char *)NULL;
  170.         }
  171.     }
  172.  
  173.     FileSlotsUsed = 0;
  174. }
  175.  
  176. /*
  177.  *    This routine frees ALL the memory associated with the dependencies
  178.  *    area.
  179.  */
  180.  
  181. void
  182. ExitDependencies(void)
  183. {
  184.     if (FileSlot == (char **)NULL)
  185.     {
  186.         return;
  187.     }
  188.  
  189.     ClearDependencies();
  190.  
  191.     free(FileSlot);
  192.  
  193.     FileSlot = (char **)NULL;
  194. }
  195.  
  196. /*
  197.  *    This file adds a file name to the current dependency list, provided
  198.  *    it is not already in the list.  It returns TRUE if successful, FALSE
  199.  *    if out of memory.
  200.  */
  201.  
  202. int
  203. AddDependency(const char *pFile)
  204. {
  205.     char **newList;
  206.     unsigned int i;
  207.  
  208.     assert(FileSlot != (char **)NULL);
  209.  
  210.     /*
  211.      *    Check the existing slots for a match
  212.      */
  213.  
  214.     for (i = 0; i < FileSlotsUsed; i++)
  215.     {
  216.         if (strcmp(FileSlot[i], pFile) == 0)
  217.         {
  218.             return(TRUE);
  219.         }
  220.     }
  221.  
  222.     /*
  223.      *    If we've used all the slots (the > part of the test is just paranoia)
  224.      *    extend the list.
  225.      */
  226.  
  227.     if (FileSlotsUsed >= FileSlotsAvail)
  228.     {
  229.         newList = (char **)realloc(FileSlot, (FileSlotsAvail + DEFAULT_ALLOC) * sizeof(char*));
  230.  
  231.         if (newList == (char **)NULL)
  232.         {
  233.             (void)fprintf(stderr, "Out of memory\n");
  234.             return(FALSE);
  235.         }
  236.  
  237.         FileSlot = newList;
  238.  
  239.         for (i = FileSlotsAvail; i < FileSlotsAvail + DEFAULT_ALLOC; i++)
  240.         {
  241.             FileSlot[i] = (char *)NULL;
  242.         }
  243.  
  244.         FileSlotsAvail += DEFAULT_ALLOC;
  245.     }
  246.  
  247.     /*
  248.      *    Tack a copy of the filename into the next available slot
  249.      */
  250.  
  251.     FileSlot[FileSlotsUsed] = malloc(strlen(pFile) + 1);
  252.     if (FileSlot[FileSlotsUsed] == (char *)NULL)
  253.     {
  254.         (void)fprintf(stderr, "Out of memory\n");
  255.         return(FALSE);
  256.     }
  257.  
  258.     (void)strcpy(FileSlot[FileSlotsUsed], pFile);
  259.     FileSlotsUsed++;
  260.  
  261.     return(TRUE);
  262. }
  263.  
  264. /*
  265.  *    This routine generates the formatted output
  266.  */
  267.  
  268. void
  269. GenerateOutput(const char *pFile, const Extension *pExt)
  270. {
  271.     unsigned int n;
  272.     unsigned int depCol;
  273.     unsigned int curCol;
  274.     size_t i;
  275.     size_t len;
  276.  
  277.     depCol = ((MAX_FILE_LEN + 2 + TabSize - 1) / TabSize) * TabSize;
  278.  
  279.     len = strlen(pFile) - pExt->srcExtLen;
  280.     curCol = 0;
  281.  
  282.     for (i = 0; i < len; i++)
  283.     {
  284.         putchar(pFile[i]);
  285.         curCol++;
  286.     }
  287.  
  288.     (void)fputs(pExt->objExt, stdout);
  289.     curCol += pExt->objExtLen;
  290.  
  291.     putchar(':');
  292.     curCol++;
  293.  
  294.     while(curCol < depCol)
  295.     {
  296.         putchar('\t');
  297.         curCol = ((curCol + TabSize) / TabSize) * TabSize;
  298.     }
  299.  
  300.     for (i = 0; i < len; i++)
  301.     {
  302.         putchar(pFile[i]);
  303.         curCol++;
  304.     }
  305.  
  306.     (void)fputs(pExt->srcExt, stdout);
  307.     curCol += pExt->srcExtLen;
  308.  
  309.     for (n = 0; n < FileSlotsUsed; n++)
  310.     {
  311.         len = strlen(FileSlot[n]);
  312.         if (curCol + len + 1 >= LineWidth - 2)
  313.         {
  314.             putchar(' ');
  315.             putchar('\\');
  316.             putchar('\n');
  317.             curCol = 0;
  318.             while(curCol < depCol)
  319.             {
  320.                 putchar('\t');
  321.                 curCol = ((curCol + TabSize) / TabSize) * TabSize;
  322.             }
  323.         }
  324.         else
  325.         {
  326.             putchar(' ' );
  327.         }
  328.         (void)fputs(FileSlot[n], stdout);
  329.         
  330.         curCol += len + 1;
  331.     }
  332.  
  333.     putchar('\n');
  334. }
  335.  
  336. /*
  337.  *    This is the "C" syntax file scanner.  It looks for lines containg
  338.  *    #include "file".  Lines of the format #include <file> are ignored.
  339.  *    New dependencies found are tacked on the end of the list, and then
  340.  *    This list is itself parsed.  Because of this approach, it never
  341.  *    recurses more than two levels.  At most, the list grows ahead of the
  342.  *    point at which it is processing.
  343.  */
  344.  
  345. int
  346. ScanFileC(const char *pFile, int nested, const Extension *pExt)
  347. {
  348.     FILE *fp;
  349.     char lineBuffer[LINE_BUFFER_SIZE + 1];
  350.     long lineNum;
  351.     char *pStart;
  352.     char *pEnd;
  353.     unsigned int i;
  354.  
  355.     if (!nested)
  356.     {
  357.         ClearDependencies();
  358.     }
  359.  
  360.     fp = fopen(pFile, "r");
  361.     if (fp == (FILE *)NULL)
  362.     {
  363.         if (nested)
  364.         {
  365.             (void)fprintf(stderr, "hfiles: Warning: can't open '%s'\n", pFile);
  366.             return(TRUE);
  367.         }
  368.         else
  369.         {
  370.             (void)fprintf(stderr, "hfiles: Can't open '%s'\n", pFile);
  371.             return(FALSE);
  372.         }
  373.     }
  374.  
  375.     lineNum = 0;
  376.  
  377.     while(fgets(lineBuffer, LINE_BUFFER_SIZE, fp) != (char *)NULL)
  378.     {
  379.         ++lineNum;
  380.  
  381.         if (!WholeLine(lineBuffer))
  382.         {
  383.             (void)fprintf(stderr,
  384.                             "hfiles: '%s' line %ld too long\n",
  385.                             pFile,
  386.                             lineNum);
  387.             return(FALSE);
  388.         }
  389.  
  390.         pStart = lineBuffer;
  391.  
  392.         while(*pStart == ' ' || *pStart == '\t')
  393.         {
  394.             ++pStart;
  395.         }
  396.  
  397.         if (strncmp(pStart, "#include", 8) == 0)
  398.         {
  399.             pStart += 8;
  400.  
  401.             while(*pStart == ' ' || *pStart == '\t')
  402.             {
  403.                 ++pStart;
  404.             }
  405.  
  406.             if (*pStart == '"')
  407.             {
  408.                 ++pStart;
  409.                 pEnd = pStart;
  410.  
  411.                 while(*pEnd != '"' && *pEnd != '\0')
  412.                 {
  413.                     ++pEnd;
  414.                 }
  415.  
  416.                 if (*pEnd == '"')
  417.                 {
  418.                     *pEnd = '\0';
  419.  
  420.                     if (!AddDependency(pStart))
  421.                     {
  422.                         (void)fclose(fp);
  423.                         return(FALSE);
  424.                     }
  425.                 }
  426.             }
  427.         }
  428.     }
  429.  
  430.     (void)fclose(fp);
  431.     if (nested)
  432.     {
  433.         return(TRUE);
  434.     }
  435.  
  436.     for (i = 0; i < FileSlotsUsed; i++)
  437.     {
  438.         if (!ScanFileC(FileSlot[i], TRUE, pExt))
  439.         {
  440.             return(FALSE);
  441.         }
  442.     }
  443.  
  444.     GenerateOutput(pFile, pExt);
  445.     return(TRUE);
  446. }
  447.  
  448. /*
  449.  *    This is the assembly format processor.  It looks for lines like:
  450.  *        include file
  451.  *    and accepts either "include" or "INCLUDE".
  452.  */
  453.  
  454. int
  455. ScanFileA(const char *pFile, int nested, const Extension *pExt)
  456. {
  457.     FILE *fp;
  458.     char lineBuffer[LINE_BUFFER_SIZE + 1];
  459.     long lineNum;
  460.     char *pStart;
  461.     char *pEnd;
  462.     unsigned int i;
  463.  
  464.     if (!nested)
  465.     {
  466.         ClearDependencies();
  467.     }
  468.  
  469.     fp = fopen(pFile, "r");
  470.     if (fp == (FILE *)NULL)
  471.     {
  472.         if (nested)
  473.         {
  474.             (void)fprintf(stderr, "hfiles: Warning: can't open '%s'\n", pFile);
  475.             return(TRUE);
  476.         }
  477.         else
  478.         {
  479.             (void)fprintf(stderr, "hfiles: Can't open '%s'\n", pFile);
  480.             return(FALSE);
  481.         }
  482.     }
  483.  
  484.     lineNum = 0;
  485.  
  486.     while(fgets(lineBuffer, LINE_BUFFER_SIZE, fp) != (char *)NULL)
  487.     {
  488.         ++lineNum;
  489.  
  490.         if (!WholeLine(lineBuffer))
  491.         {
  492.             (void)fprintf(stderr,
  493.                             "hfiles: '%s' line %ld too long\n",
  494.                             pFile,
  495.                             lineNum);
  496.             return(FALSE);
  497.         }
  498.  
  499.         pStart = lineBuffer;
  500.  
  501.         while(*pStart == ' ' || *pStart == '\t')
  502.         {
  503.             ++pStart;
  504.         }
  505.  
  506.         if (strncmp(pStart, "include", 7) == 0 ||
  507.             strncmp(pStart, "INCLUDE", 7) == 0)
  508.         {
  509.             pStart += 7;
  510.  
  511.             while(*pStart == ' ' || *pStart == '\t')
  512.             {
  513.                 ++pStart;
  514.             }
  515.  
  516.             pEnd = pStart;
  517.  
  518.             while(*pEnd != ' ' && *pEnd != '\t' && *pEnd != ';' && *pEnd != '\0')
  519.             {
  520.                 ++pEnd;
  521.             }
  522.  
  523.             *pEnd = '\0';
  524.  
  525.             if (!AddDependency(pStart))
  526.             {
  527.                 (void)fclose(fp);
  528.                 return(FALSE);
  529.             }
  530.         }
  531.     }
  532.  
  533.     (void)fclose(fp);
  534.     if (nested)
  535.     {
  536.         return(TRUE);
  537.     }
  538.  
  539.     for (i = 0; i < FileSlotsUsed; i++)
  540.     {
  541.         if (!ScanFileA(FileSlot[i], TRUE, pExt))
  542.         {
  543.             return(FALSE);
  544.         }
  545.     }
  546.  
  547.     GenerateOutput(pFile, pExt);
  548.     return(TRUE);
  549. }
  550.  
  551. /*
  552.  *    This routine processes command line arguments and arguments received
  553.  *    from command files.  These items can be file names or options.
  554.  */
  555.  
  556. int
  557. ProcessArgument(const char *pArg)
  558. {
  559.     FILE *fp;
  560.     const Extension *pExt;
  561.     size_t len;
  562.     char lineBuffer[LINE_BUFFER_SIZE + 1];
  563.     long lineNum;
  564.  
  565.     while(*pArg == ' ' || *pArg == '\t')
  566.     {
  567.         ++pArg;
  568.     }
  569.  
  570.     if (*pArg == '@')
  571.     {
  572.         fp = fopen(&pArg[1], "r");
  573.         if (fp == (FILE *)NULL)
  574.         {
  575.             (void)fprintf(stderr, "hfiles: Can't open '%s'\n", &pArg[1]);
  576.             return(FALSE);
  577.         }
  578.  
  579.         lineNum = 0;
  580.  
  581.         while(fgets(lineBuffer, LINE_BUFFER_SIZE, fp) != (char *)NULL)
  582.         {
  583.             ++lineNum;
  584.  
  585.             if (!WholeLine(lineBuffer))
  586.             {
  587.                 (void)fprintf(stderr,
  588.                                 "hfiles: '%s' line %ld too long\n",
  589.                                 &pArg[1],
  590.                                 lineNum);
  591.                 return(FALSE);
  592.             }
  593.  
  594.             if (!ProcessArgument(lineBuffer))
  595.             {
  596.                 (void)fclose(fp);
  597.                 return(FALSE);
  598.             }
  599.         }
  600.  
  601.         (void)fclose(fp);
  602.         return(TRUE);
  603.     }
  604.  
  605.     if (*pArg == '-')
  606.     {
  607.         switch(pArg[1])
  608.         {
  609.         case 't': case 'T':
  610.             if (sscanf(&pArg[2], "%u", &TabSize) != 1)
  611.             {
  612.                 (void)fprintf(stderr, "Invalid tab size: '%s'\n", &pArg[2]);
  613.                 return(FALSE);
  614.             }
  615.             break;
  616.  
  617.         case 'l': case 'L':
  618.             if (sscanf(&pArg[2], "%u", &LineWidth) != 1)
  619.             {
  620.                 (void)fprintf(stderr, "Invalid line width: '%s'\n", &pArg[2]);
  621.                 return(FALSE);
  622.             }
  623.             break;
  624.  
  625.         default:
  626.             (void)fprintf(stderr, "Unknown option: '%s'\n", pArg);
  627.             return(FALSE);
  628.         }
  629.  
  630.         return(TRUE);
  631.     }
  632.  
  633.     len = strlen(pArg);
  634.  
  635.     for(pExt = ExtList; pExt->scanner != SCANNER_EOL; pExt++)
  636.     {
  637.         if (len > pExt->srcExtLen)
  638.         {
  639.             if (strcmp(&pArg[len - pExt->srcExtLen], pExt->srcExt) == 0)
  640.             {
  641.                 switch(pExt->scanner)
  642.                 {
  643.                 case SCANNER_CTYPE:
  644.                     return(ScanFileC(pArg, FALSE, pExt));
  645.  
  646.                 case SCANNER_ATYPE:
  647.                     return(ScanFileA(pArg, FALSE, pExt));
  648.  
  649.                 default:
  650.                     assert(FALSE);
  651.                 }
  652.             }
  653.         }
  654.     }
  655.  
  656.     (void)fprintf(stderr, "'%s' does not have a recognized extension\n", pArg);
  657.     return(FALSE);
  658. }
  659.  
  660. void
  661. Usage(void)
  662. {
  663.     (void)fprintf(stderr, "Usage:\thfiles [options] files...\n");
  664.     (void)fprintf(stderr, "\tOptions:\t-tN\tN char tab stops\n");
  665.     (void)fprintf(stderr, "\t\t\t-lN\tOutput line width of N chars\n");
  666.     (void)fprintf(stderr, "\t\t\t@file\tTake arguments from file\n");
  667. }
  668.  
  669. int
  670. main(int argc, char **argv)
  671. {
  672.     int i;
  673.  
  674.     if (argc == 1)
  675.     {
  676.         Usage();
  677.         return(1);
  678.     }
  679.  
  680.     if (!InitDependencies())
  681.     {
  682.         return(1);
  683.     }
  684.  
  685.     for (i = 1; i < argc; i++)
  686.     {
  687.         if (!ProcessArgument(argv[i]))
  688.         {
  689.             return(1);
  690.         }
  691.     }
  692.  
  693.     ExitDependencies();
  694.     return(0);
  695. }
  696.